#include "preHeader.h"
#include "RankMgr.h"
#include "Session/C2RankPacketHandler.h"
#include "Session/S2RankPacketHandler.h"
#include "Session/GameServerMgr.h"
#include "Game/TransMgr.h"
#include "Game/RPCHelper.h"
#include "Game/PacketDispatcher.h"

namespace std {
#define COMPARE_GREATER_LESS(FieldName,LastTime) \
	do { \
		if (p1->FieldName > p2->FieldName) return true; \
		if (p1->FieldName < p2->FieldName) return false; \
		if (p1->LastTime < p2->LastTime) return true; \
		if (p1->LastTime > p2->LastTime) return false; \
		return false; \
	} while (0)
	template <> struct less<RankData4Player*> {
	public:
		less(RANK_SUBTYPE subType) : m_subType(subType) {}
		bool operator()(const RankData4Player* p1, const RankData4Player* p2) const {
			switch (m_subType) {
			case RANK_SUBTYPE::PLAYER_LEVEL:
				COMPARE_GREATER_LESS(lastLevel, lastLevelTime);
				break;
			case RANK_SUBTYPE::PLAYER_FIGHT_VALUE:
				COMPARE_GREATER_LESS(lastFightValue, lastFightValueTime);
				break;
			}
			return false;
		}
	private:
		const RANK_SUBTYPE m_subType;
	};
	template <> struct less<RankData4Guild*> {
	public:
		less(RANK_SUBTYPE subType) : m_subType(subType) {}
		bool operator()(const RankData4Guild* p1, const RankData4Guild* p2) const {
			switch (m_subType) {
			case RANK_SUBTYPE::GUILD_LEVEL:
				COMPARE_GREATER_LESS(lastLevel, lastLevelTime);
				break;
			case RANK_SUBTYPE::GUILD_FIGHT_VALUE:
				COMPARE_GREATER_LESS(lastFightValue, lastFightValueTime);
				break;
			}
			return false;
		}
	private:
		const RANK_SUBTYPE m_subType;
	};
#undef COMPARE_GREATER_LESS
}

RankMgr::RankMgr()
: IServerService("RankMgr")
, m_rank4Player{
	{RANK_SUBTYPE::PLAYER_LEVEL},
	{RANK_SUBTYPE::PLAYER_FIGHT_VALUE}}
, m_rank4Guild{
	{RANK_SUBTYPE::GUILD_LEVEL},
	{RANK_SUBTYPE::GUILD_FIGHT_VALUE}}
{
	assert(m_rank4Player.size() == (u8)RANK_SUBTYPE::PLAYER_COUNT);
	assert(m_rank4Guild.size() == (u8)RANK_SUBTYPE::GUILD_COUNT);
}

RankMgr::~RankMgr()
{
	ClearAllPlayerRankData();
	ClearAllGuildRankData();
}

int RankMgr::HandleClientPacket(uint32 uid, INetPacket& pck)
{
	return sC2RankPacketHandler.HandlePacket(this, pck, uid);
}

int RankMgr::HandleServerPacket(INetPacket& pck)
{
	return sS2RankPacketHandler.HandlePacket(this, pck);
}

void RankMgr::ClearAllPlayerRankData()
{
	for (auto& rank : m_rank4Player) {
		rank.Clear();
	}
	for (auto&[playerId, pRankData] : m_rankData4Player) {
		delete pRankData;
	}
	m_rankData4Player.clear();
}

void RankMgr::ClearAllGuildRankData()
{
	for (auto& rank : m_rank4Guild) {
		rank.Clear();
	}
	for (auto&[guildId, pRankData] : m_rankData4Guild) {
		delete pRankData;
	}
	m_rankData4Guild.clear();
}

void RankMgr::Update(uint64 diffTime)
{
	IServerService::UpdateAllPackets();
	AsyncTaskOwner::UpdateTask();
}

GErrorCode RankMgr::HandleNewPlayer(INetPacket& pck)
{
	auto playerId = pck.Read<uint32>();
	auto itr = m_rankData4Player.find(playerId);
	if (itr != m_rankData4Player.end()) {
		return CommonInternalError;
	}

	auto pRankData = new RankData4Player;
	pRankData->playerId = playerId;
	pck >> pRankData->lastLevel >> pRankData->lastLevelTime
		>> pRankData->lastFightValue >> pRankData->lastFightValueTime;
	m_rankData4Player.emplace(playerId, pRankData);

	for (auto& rank : m_rank4Player) {
		rank.Add(playerId, std::move(pRankData));
	}

	return CommonSuccess;
}

GErrorCode RankMgr::HandleUpdatePlayer(INetPacket& pck)
{
	auto playerId = pck.Read<uint32>();
	auto itr = m_rankData4Player.find(playerId);
	if (itr == m_rankData4Player.end()) {
		return CommonInternalError;
	}

#define UPDATE_RANK_GREATER_LESS(FieldName,LastTime) \
	do { \
		m_rank4Player[subType].Dirty(playerId, [&pck](auto pRankData) { \
			RankData4Player oldRankData; \
			oldRankData.FieldName = pRankData->FieldName; \
			oldRankData.LastTime = pRankData->LastTime; \
			pck >> pRankData->FieldName >> pRankData->LastTime; \
			if (pRankData->FieldName > oldRankData.FieldName) return -1; \
			if (pRankData->FieldName < oldRankData.FieldName) return 1; \
			if (pRankData->LastTime < oldRankData.LastTime) return -1; \
			if (pRankData->LastTime > oldRankData.LastTime) return 1; \
			return 0; \
		}); \
	} while (0)

	auto pRankData = itr->second;
	auto subType = pck.Read<uint8>();
	switch ((RANK_SUBTYPE)subType) {
	case RANK_SUBTYPE::PLAYER_LEVEL:
		UPDATE_RANK_GREATER_LESS(lastLevel, lastLevelTime);
		break;
	case RANK_SUBTYPE::PLAYER_FIGHT_VALUE:
		UPDATE_RANK_GREATER_LESS(lastFightValue, lastFightValueTime);
		break;
	}

#undef UPDATE_RANK_GREATER_LESS

	return CommonSuccess;
}

GErrorCode RankMgr::HandleRemovePlayer(INetPacket& pck)
{
	auto playerId = pck.Read<uint32>();
	auto itr = m_rankData4Player.find(playerId);
	if (itr == m_rankData4Player.end()) {
		return CommonInternalError;
	}

	for (auto& rank : m_rank4Player) {
		rank.Remove(playerId);
	}

	auto pRankData = itr->second;
	m_rankData4Player.erase(itr);
	delete pRankData;

	return CommonSuccess;
}

GErrorCode RankMgr::HandleNewGuild(INetPacket& pck)
{
	auto guildId = pck.Read<uint32>();
	auto itr = m_rankData4Guild.find(guildId);
	if (itr != m_rankData4Guild.end()) {
		return CommonInternalError;
	}

	auto pRankData = new RankData4Guild;
	pRankData->guildId = guildId;
	pck >> pRankData->lastLevel >> pRankData->lastLevelTime
		>> pRankData->lastFightValue >> pRankData->lastFightValueTime;
	m_rankData4Guild.emplace(guildId, pRankData);

	for (auto& rank : m_rank4Guild) {
		rank.Add(guildId, std::move(pRankData));
	}

	return CommonSuccess;
}

GErrorCode RankMgr::HandleUpdateGuild(INetPacket& pck)
{
	auto guildId = pck.Read<uint32>();
	auto itr = m_rankData4Guild.find(guildId);
	if (itr == m_rankData4Guild.end()) {
		return CommonInternalError;
	}

#define UPDATE_RANK_GREATER_LESS(FieldName,LastTime) \
	do { \
		m_rank4Guild[subType].Dirty(guildId, [&pck](auto pRankData) { \
			RankData4Guild oldRankData; \
			oldRankData.FieldName = pRankData->FieldName; \
			oldRankData.LastTime = pRankData->LastTime; \
			pck >> pRankData->FieldName >> pRankData->LastTime; \
			if (pRankData->FieldName > oldRankData.FieldName) return -1; \
			if (pRankData->FieldName < oldRankData.FieldName) return 1; \
			if (pRankData->LastTime < oldRankData.LastTime) return -1; \
			if (pRankData->LastTime > oldRankData.LastTime) return 1; \
			return 0; \
		}); \
	} while (0)

	auto pRankData = itr->second;
	auto subType = pck.Read<uint8>();
	switch ((RANK_SUBTYPE)subType) {
	case RANK_SUBTYPE::GUILD_LEVEL:
		UPDATE_RANK_GREATER_LESS(lastLevel, lastLevelTime);
		break;
	case RANK_SUBTYPE::GUILD_FIGHT_VALUE:
		UPDATE_RANK_GREATER_LESS(lastFightValue, lastFightValueTime);
		break;
	}

#undef UPDATE_RANK_GREATER_LESS

	return CommonSuccess;
}

GErrorCode RankMgr::HandleRemoveGuild(INetPacket& pck)
{
	auto guildId = pck.Read<uint32>();
	auto itr = m_rankData4Guild.find(guildId);
	if (itr == m_rankData4Guild.end()) {
		return CommonInternalError;
	}

	for (auto& rank : m_rank4Guild) {
		rank.Remove(guildId);
	}

	auto pRankData = itr->second;
	m_rankData4Guild.erase(itr);
	delete pRankData;

	return CommonSuccess;
}

GErrorCode RankMgr::HandleGetPlayerRankList(Coroutine::YieldContext& ctx,
	uint32 playerId, uint8 subType, uint32 startIndex, uint32 getCountMax)
{
	if (subType >= (uint8)RANK_SUBTYPE::PLAYER_COUNT) {
		return InvalidRequest;
	}
	if (getCountMax > GET_RANK_COUNT_MAX) {
		return InvalidRequest;
	}

	auto& rank = m_rank4Player[subType];
	auto selfRank = rank.GetRank(playerId);

	size_t n = 0;
	uint32 playerIds[GET_RANK_COUNT_MAX];
	NetPacket rpcReqPck(SGX_PULL_CHARACTER_INFOS);
	rpcReqPck << (u32)0 << (u32)CharValueType::Name;
	for (auto& node : MakeIteratorRange(rank.advance(startIndex), rank.end())) {
		rpcReqPck << node.key;
		playerIds[n] = node.key;
		if (++n >= getCountMax) {
			break;
		}
	}
	auto errCode = sGameServerMgr.RPCInvoke2GS(
		rpcReqPck, ctx.GetRPCInvokeCb(), this, DEF_S2S_RPC_TIMEOUT);
	if (errCode != RPCErrorNone) {
		return CommonInternalError;
	}
	auto resp = ctx.WaitRPCInvokeResp();
	if (resp.err != RPCErrorNone) {
		return CommonInternalError;
	}

	auto dataDict = RPCHelper::ReadReplyDataDict(*resp.pck);
	NetPacket toClient(SMSG_RANK_GET_PLAYER_LIST_RESP);
	toClient << subType << (u32)selfRank << startIndex << (n >= getCountMax);
	for (size_t i = 0; i < n; ++i) {
		auto pRankData = GetRankData4Player(playerIds[i]);
		if (pRankData == NULL) {
			continue;
		}
		auto& dataStr = RPCHelper::TryGetReplyData(dataDict, playerIds[i]);
		if (dataStr.empty()) {
			continue;
		}
		toClient << pRankData->playerId;
		toClient.Append(dataStr.data(), dataStr.size());
		PackRankData4Player(toClient, pRankData, subType);
	}
	sTransMgr.SendPacket2ClientSafe(playerId, toClient);

	return CommonSuccess;
}

GErrorCode RankMgr::HandleGetGuildRankList(Coroutine::YieldContext& ctx,
	uint32 playerId, uint32 guildId, uint8 subType, uint32 startIndex, uint32 getCountMax)
{
	if (subType >= (uint8)RANK_SUBTYPE::GUILD_COUNT) {
		return InvalidRequest;
	}
	if (getCountMax > GET_RANK_COUNT_MAX) {
		return InvalidRequest;
	}

	auto& rank = m_rank4Guild[subType];
	auto selfRank = guildId != 0 ? rank.GetRank(guildId) : 0;

	size_t n = 0;
	uint32 guildIds[GET_RANK_COUNT_MAX];
	NetPacket rpcReqPck(CGX_PULL_GUILD_INFOS);
	rpcReqPck << (u8)SocialRPCReply::Rank << (u32)0 << (u32)GuildValueType::Name;
	for (auto& node : MakeIteratorRange(rank.advance(startIndex), rank.end())) {
		rpcReqPck << node.key;
		guildIds[n] = node.key;
		if (++n >= getCountMax) {
			break;
		}
	}
	sPacketDispatcher.RPCInvoke2Guild(rpcReqPck, ctx.GetRPCInvokeCb(), this);
	auto resp = ctx.WaitRPCInvokeResp();
	if (resp.err != RPCErrorNone) {
		return CommonInternalError;
	}

	auto dataDict = RPCHelper::ReadReplyDataDict(*resp.pck);
	NetPacket toClient(SMSG_RANK_GET_GUILD_LIST_RESP);
	toClient << subType << (u32)selfRank << startIndex << (n >= getCountMax);
	for (size_t i = 0; i < n; ++i) {
		auto pRankData = GetRankData4Guild(guildIds[i]);
		if (pRankData == NULL) {
			continue;
		}
		auto& dataStr = RPCHelper::TryGetReplyData(dataDict, guildIds[i]);
		if (dataStr.empty()) {
			continue;
		}
		toClient << pRankData->guildId;
		toClient.Append(dataStr.data(), dataStr.size());
		PackRankData4Guild(toClient, pRankData, subType);
	}
	sTransMgr.SendPacket2ClientSafe(playerId, toClient);

	return CommonSuccess;
}

GErrorCode RankMgr::HandlePackGuildRankValue(
	INetPacket& pck, uint8 subType, INetPacket& source)
{
	if (subType >= (uint8)RANK_SUBTYPE::GUILD_COUNT) {
		return InvalidRequest;
	}

	auto& rank = m_rank4Guild[subType];
	while (!source.IsReadableEmpty()) {
		pck << (u32)rank.GetRank(source.Read<uint32>());
	}

	return CommonSuccess;
}

GErrorCode RankMgr::HandlePackGuildRankIdList(
	INetPacket& pck, uint8 subType, uint32 startIndex, uint32 getCountMax)
{
	if (subType >= (uint8)RANK_SUBTYPE::GUILD_COUNT) {
		return InvalidRequest;
	}

	size_t n = 0;
	auto& rank = m_rank4Guild[subType];
	for (auto& node : MakeIteratorRange(rank.advance(startIndex), rank.end())) {
		pck << node.key;
		if (++n >= getCountMax) {
			break;
		}
	}

	return CommonSuccess;
}

RankData4Player* RankMgr::GetRankData4Player(uint32 playerId) const
{
	auto itr = m_rankData4Player.find(playerId);
	return itr != m_rankData4Player.end() ? itr->second : NULL;
}

void RankMgr::PackRankData4Player(INetPacket& pck, const RankData4Player* pRankData, uint8 subType)
{
	switch ((RANK_SUBTYPE)subType) {
	case RANK_SUBTYPE::PLAYER_LEVEL:
		pck << pRankData->lastLevel << pRankData->lastLevelTime;
		break;
	case RANK_SUBTYPE::PLAYER_FIGHT_VALUE:
		pck << pRankData->lastFightValue << pRankData->lastFightValueTime;
		break;
	}
}

RankData4Guild* RankMgr::GetRankData4Guild(uint32 playerId) const
{
	auto itr = m_rankData4Guild.find(playerId);
	return itr != m_rankData4Guild.end() ? itr->second : NULL;
}

void RankMgr::PackRankData4Guild(INetPacket& pck, const RankData4Guild* pRankData, uint8 subType)
{
	switch ((RANK_SUBTYPE)subType) {
	case RANK_SUBTYPE::GUILD_LEVEL:
		pck << pRankData->lastLevel << pRankData->lastLevelTime;
		break;
	}
}
